package com.wsjzzcbq.wsjzcode.execute;

import com.alibaba.fastjson2.JSONObject;
import com.wsjzzcbq.wsjzcode.bean.Code;
import com.wsjzzcbq.wsjzcode.bean.ExecuteResult;
import com.wsjzzcbq.wsjzcode.code.CodeRunResultMap;
import com.wsjzzcbq.wsjzcode.compile.SqlCompiler;
import com.wsjzzcbq.wsjzcode.consts.SqlTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * SqlExecuter
 *
 * @author wsjz
 * @date 2023/07/07
 */
@Slf4j
@Component
public class SqlExecuter extends SqlExecuterWrapper{

    @Autowired
    private DataSource dataSource;
    @Autowired
    private SqlCompiler sqlCompiler;

    @Override
    public void exec(Code code) {
        Connection connection = null;
        PreparedStatement ps = null;
        ExecuteResult result = new ExecuteResult();
        try {
            connection = dataSource.getConnection();
            ps = connection.prepareStatement(code.getCode());
            SqlTypeEnum sqlTypeEnum = sqlCompiler.sqlType(code.getCode());
            switch (sqlTypeEnum) {
                case SELECT:
                    execSelect(ps, code.getNumber(), result);
                    break;
                case INSERT:
                    execInsert(ps, code.getNumber(), result);
                    break;
                case UPDATE:
                    execUpdate(ps, code.getNumber(), result);
                    break;
                case DELETE:
                    execDelete(ps, code.getNumber(), result);
                default:
            }
        } catch (SQLException e) {
            result.setResult(ExecuteResult.ERROR);
            result.setMsg(e.getMessage());
            CodeRunResultMap.map.put(code.getNumber(), result);
            log.error(e.getMessage(), e);
        } finally {
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void execSelect(PreparedStatement ps, String number, ExecuteResult result) throws SQLException {
        ResultSet rs = ps.executeQuery();
        int columnCount = rs.getMetaData().getColumnCount();
        List<String> header = new LinkedList<>();
        for (int i = 1; i <= columnCount; i++) {
            header.add(rs.getMetaData().getColumnName(i));
        }
        List<Map<String, Object>> list = new LinkedList<>();
        while (rs.next()) {
            Map<String, Object> map = new HashMap<>(columnCount);
            for (int i = 1; i <= columnCount; i++) {
                map.put(rs.getMetaData().getColumnName(i), rs.getObject(i));
            }
            list.add(map);
        }
        JSONObject json = new JSONObject();
        json.put("sqlType", SqlTypeEnum.SELECT.getValue());
        json.put("header", header);
        json.put("list", list);
        result.setResult(ExecuteResult.SUCCESS);
        result.setData(json);
        CodeRunResultMap.map.put(number, result);

        rs.close();
    }

    private void execInsert(PreparedStatement ps, String number, ExecuteResult result) throws SQLException {
        super.execSqlUpdate(ps, SqlTypeEnum.INSERT, number, result);
    }

    private void execUpdate(PreparedStatement ps, String number, ExecuteResult result) throws SQLException {
        super.execSqlUpdate(ps, SqlTypeEnum.UPDATE, number, result);
    }

    private void execDelete(PreparedStatement ps, String number, ExecuteResult result) throws SQLException {
        super.execSqlUpdate(ps, SqlTypeEnum.DELETE, number, result);
    }
}
